#include <string>
#include <iostream>
#include <filesystem>
#include <windows.h>
#include <detours.h>
#include <fstream>
#include <vector>
#include <time.h>
#include <stdlib.h>

#define path filesystem::path
#define diriterator filesystem::directory_iterator
#define sfilename filename().string

using namespace std;

string nlist[101];
char c = 1;

__int64(__cdecl* original)(void* Dst, void* Src, char a3) = (__int64(__cdecl*)(void* Dst, void* Src, char a3))0x140622EE0;

void InjectCode(void* address, const std::vector<uint8_t> data)
{
	const size_t byteCount = data.size() * sizeof(uint8_t);

	DWORD oldProtect;
	VirtualProtect(address, byteCount, PAGE_EXECUTE_READWRITE, &oldProtect);
	memcpy(address, data.data(), byteCount);
	VirtualProtect(address, byteCount, oldProtect, nullptr);
}

void lfiles()
{
	try {
		for (path b : diriterator(+"../rom/sound/bgm/"))
		{
			if (path(b).sfilename().length() == 14 && path(b).sfilename().substr(0, 12) == "selector_ft_"
				&& path(b).sfilename().at(13) >= '0' && path(b).sfilename().at(13) <= '9'
				&& path(b).sfilename().at(14) >= '0' && path(b).sfilename().at(14) <= '9')
			{
				nlist[c] = path(b).sfilename().substr(13, 2);
				c++;
			}
		}

		for (path p : diriterator("../mdata"))
		{
			if (path(p).sfilename().at(0) != 'M' || path(p).sfilename().length() > 4) continue;
			try {
				for (path q : diriterator(p.string() + "/rom/sound/bgm/"))
				{
					if (path(q).sfilename().length() == 18 && path(q).sfilename().substr(0, 12) == "selector_ft_"
						&& isdigit(path(q).sfilename().at(12))
						&& isdigit(path(q).sfilename().at(13))
						)
					{
						bool exists = false;
						for (char i = 0; i < c; i++)
						{
							if (nlist[i] == path(q).sfilename().substr(13, 2))
							{
								exists = true;
								break;
							}
						}
						if (!exists)
						{
							nlist[c] = path(q).sfilename().substr(12, 2);
							c++;
						}
					}
				}
			}
			catch (const filesystem::filesystem_error & e) {
				//cout << "[bgm-random] File system error " << e.what() << " " << e.path1() << " " << e.path2() << " " << e.code() << endl;
			}
		}
	}
	catch (const filesystem::filesystem_error & e) {
		cout << "[bgm-random] File system error " << e.what() << " " << e.path1() << " " << e.path2() << " " << e.code() << endl;
	}
}

__int64 hooked(void* Dst, void* Src, char a3)
{
	string srandom = nlist[rand() % 100];;
	unsigned char a = srandom.at(0);
	unsigned char b = srandom.at(1);
	InjectCode((int*)0x140A33912, { a, b });
	InjectCode((int*)0x140A2F8E0, { a, b });
	InjectCode((int*)0x140A2F8F0, { a, b });
	InjectCode((int*)0x140A2F900, { a, b });
	cout << "[bgm-random] Song: " << srandom << endl;
	return original(Dst, Src, a3);
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
	if (ul_reason_for_call == DLL_PROCESS_ATTACH)
	{
		srand(time(NULL));
		lfiles();
		cout << "[bgm-random] Random songs: " << to_string(c) << endl;
		if (c > 1)
		{
			nlist[0] = "lp";
			DisableThreadLibraryCalls(hModule);
			DetourTransactionBegin();
			DetourUpdateThread(GetCurrentThread());
			cout << "[bgm-random] Hooking functions..." << endl;
			DetourAttach(&(PVOID&)original, (PVOID)hooked);
			cout << "[bgm-random] Functions hooked." << endl;
			DetourTransactionCommit();
		}
	}
	return TRUE;
}
